#include "Wireless.h"
#include "SX127X.h"
#include "Sys.h"

#define WIRELESS_PROTO_HEAD 0x5CB5
#define WIRELESS_PROTO_VERSION 0x01
#define MHz(x) (x * 1000000)

#pragma pack(1)
typedef struct
{
    uint16_t head;
    uint32_t addr;
    uint8_t version;
    uint8_t device;
    uint8_t cmd;
    uint8_t data[];
} WirelessProto_t;
#pragma pack()

typedef struct
{
    bool fixed;
    uint8_t oldch;
    uint8_t channel;
    uint8_t count;
    SysTime_t holdTime;
} WirelessConflict_t;

static const int FREQ_TABLE[WIRELESS_CHANNEL_COUNT] = {
    MHz(477),
    MHz(480),
    MHz(483),
    MHz(486),
    MHz(489),
    MHz(492),
    MHz(495),
    MHz(498),
    MHz(501),
    MHz(504)};

static SX127X_t g_sx1276Config;
static WirelessDataRecv_f g_recvHandle;
static WirelessConflict_t g_conflict;

extern SPI_HandleTypeDef hspi2;

static uint8_t g_randomChannel[WIRELESS_CHANNEL_COUNT];
static void makeRandomChannel(void)
{
    int random_nums[WIRELESS_CHANNEL_COUNT] = {0}; // 存储已经生成的随机数
    int random_num, i;
    uint8_t uuid[HAL_UUID_LEN];
    int seed;

    HalGetUUID(uuid);
    seed = uuid[0] + uuid[HAL_UUID_LEN - 1] + SysWirelessAddressGet();
    srand(seed); // 随机数种子
    for (i = 0; i < WIRELESS_CHANNEL_COUNT; i++)
    {
        do
        {
            random_num = rand() % WIRELESS_CHANNEL_COUNT;           // 取0到11之间的随机数
        } while (random_nums[random_num] == 1); // 如果已经存在，则重新生成
        random_nums[random_num] = 1;            // 标记已经生成的随机数
        g_randomChannel[i] = random_num;
    }
}

void WirelessInit(WirelessDataRecv_f recvHandle)
{
    g_sx1276Config.spi_bus = hspi2;
    g_sx1276Config.reset_gpio_port = GPIOD;
    g_sx1276Config.reset_pin = GPIO_PIN_10;
    g_sx1276Config.ss_gpio_port = GPIOB;
    g_sx1276Config.ss_pin = GPIO_PIN_12;

    g_sx1276Config.Current_Modulation = FSK_Modulation; // FSK调制
    g_sx1276Config.Current_Op_Mode = STANDBY;           // 随意
    g_sx1276Config.Frequency = FREQ_TABLE[WIRELESS_CHANNEL_COUNT / 2];                // 设置载波频率
    g_sx1276Config.PaRampTime = PaRampTime_40_us;       // FSK 模式下斜升/斜降时间
    g_sx1276Config.LnaGain = LnaGainG1;                 // 接收机增益设置 最大
    g_sx1276Config.LnaBoost = 0;
    g_sx1276Config.PaSelect = PA_BOOST_Pin;                    // 设置输出引脚
    g_sx1276Config.TxPower = 20;                               // 设置发射功率
    g_sx1276Config.FSK_Config.FrequencyDeviation = 25e3;       // 设置频率偏移   但频率偏差必须设定在600Hz-200kHz之间。 FreqDev +Bitrate/2 <= 250 kHz。
    g_sx1276Config.FSK_Config.Bitrate = FSK_BITRATE_25_KBPS;   // 设置比特率 通信比特率不能超过单边接收机带宽的两倍
    g_sx1276Config.FSK_Config.PreambleLength = 12;             // 前导码的长度  前导码长度设置比较特殊，接收端配置的前导码长度必须大于等于发送端的前导码长度，接收端才能接收
                                                               // 到数据，这就是基于前导码长度的数据包过滤，默认长度为 8 个字节，最小可以设置 6 个字节
    g_sx1276Config.FSK_Config.PreamblePolarity = Polarity_AA;  // 前导码的极性
    g_sx1276Config.FSK_Config.SyncWordGeneration = 1;          // 是否打开同步字
    g_sx1276Config.FSK_Config.SyncWord = 0x231236;             // 同步字
    g_sx1276Config.FSK_Config.SyncWordSize = 3;                // 同步字的长度设置(Byte)
    g_sx1276Config.FSK_Config.PacketFormat = VariableLength;   // 设置数据包的格式为可变的
    g_sx1276Config.FSK_Config.DataProcessingMode = PacketMode; // 设置数据处理模式为数据包模式
    g_sx1276Config.FSK_Config.PayloadLength = 255;             // 如果数据处理模式为数据包模式 是 RX模式下最大长度 否则是负载长度
    g_sx1276Config.FSK_Config.CrcCalculation = 1;              // 打开CRC校验
    g_sx1276Config.FSK_Config.CrcWhitenningType = CCITT_CRC;   // CRC类型
    g_sx1276Config.FSK_Config.CrcAutoclear = true;
    g_sx1276Config.FSK_Config.Encoding = Whitenning;                     // DC 消除编码/解码 Whitenning和CCITT_CRC 一对
    g_sx1276Config.FSK_Config.DataShaping = FSK_NoShaping;               // 数据整形
    g_sx1276Config.FSK_Config.AutoRestartRX = AutorestartRX_On_PLL_Lock; // 接收到有效数据包后控制接收机的自动重启

    // 接收机配置
    g_sx1276Config.FSK_Config.RxBandwidth = 60e3;      // 设置接收带宽(Hz)
    g_sx1276Config.FSK_Config.AfcBandwidth = 83.333e3; // 设置AFC(Hz)
    g_sx1276Config.FSK_Config.PreambleDetectorOn = 1;  // 前导码检测开/关
    // g_sx1276_init.FSK_Config.PreambleDetectorSize = ;              //触发中断所需检测到的前导码字节数(default 0x01)
    // g_sx1276_init.FSK_Config.PreambleDetectorTolerance = ;         //可以容忍的码片误差数量每比特 4 码片(default 0x0A)
    g_sx1276Config.FSK_Config.RssiOffset = RssiSmoothing_64_Samples; // 定义计算 RSSI 平均值所用的采样次数
    // g_sx1276_init.DioMapping1 = ;
    // g_sx1276_init.DioMapping2 = ;

    if (FSK_init(&g_sx1276Config) != HAL_OK)
    {
        // REMOTE_INFO("FSK_INIT ERROR");
    }

    if (SX127X_Config(&g_sx1276Config) != HAL_OK)
    {
        // REMOTE_INFO("SX127X_Config ERROR");
    }

    g_recvHandle = recvHandle;
}

void WirelessSendData(WirelessCmd_t cmd, uint8_t *data, uint16_t length)
{
    uint8_t buff[60];
    uint32_t i = 0;
    WirelessProto_t *proto = (WirelessProto_t *)buff;

    if (SysWirelessAddressGet() != 0)
    {
        proto->head = WIRELESS_PROTO_HEAD;
        proto->addr = SysWirelessAddressGet();
        proto->version = WIRELESS_PROTO_VERSION;
        proto->device = SysDeviceType();
        proto->cmd = cmd;
        memcpy(proto->data, data, length);
        FSK_Transmit(&g_sx1276Config, buff, sizeof(WirelessProto_t) + length);
        while (!FSK_CheckPackSent(&g_sx1276Config))
        {
            i++;
            if (i > 0xffff)
            {
                break;
            }
        }
    }
}

static void wirelessDataHandle(void)
{
    uint8_t recvBuff[63] = {0};
    bool crcerror = false;
    uint8_t packleght = 0;
    WirelessProto_t *proto;

    {
        SX127X_set_op_mode(&g_sx1276Config, RX);
        if (FSK_CheckPayloadReady(&g_sx1276Config))
        {
            FSK_ReadPacket(&g_sx1276Config, recvBuff, sizeof(recvBuff), &packleght, &crcerror);
            if (!crcerror && g_recvHandle != NULL)
            {
                proto = (WirelessProto_t *)recvBuff;
                if (proto->head == WIRELESS_PROTO_HEAD && SysWirelessAddressGet() != 0)
                {
                    if (proto->addr == SysWirelessAddressGet())
                    {
                        if(SysDeviceType() == proto->device)
                        {
                            g_recvHandle(proto->cmd, proto->data, packleght - sizeof(WirelessProto_t));
                        }
                    }
                    else
                    {
                        g_conflict.count++;
                    }
                }
            }
        }
    }
}

static void swtichChannel(uint8_t ch)
{
    sysLog("switch channel: %d\r\n", ch);
    SX127X_set_frequency(&g_sx1276Config, FREQ_TABLE[ch]);
    g_conflict.count = 0;
    g_conflict.holdTime = SysTime();
}

void WirelessConflictDetectInit(void)
{
    makeRandomChannel();
    g_conflict.oldch = WIRELESS_CHANNEL_COUNT / 2;
    g_conflict.channel = 0xff;
    g_conflict.fixed = false;
    swtichChannel(g_conflict.oldch);
}

static void fixChannel(uint8_t ch)
{
    sysLog("ch = %d, freq = %d", ch, FREQ_TABLE[ch]);
    // SysWirelessChannelFixed(ch);
    g_conflict.fixed = true;
}

uint8_t toChannel(uint8_t num)
{
    if (g_conflict.channel == 0xff)
    {
        return g_conflict.oldch;
    }
    return g_randomChannel[num];
}

uint8_t WirelessRealtimeChannel(void)
{
    return toChannel(g_conflict.channel);
}

static void wirelessConflictDetect(void)
{
    if (SysWirelessAddressGet() != 0 && !g_conflict.fixed)
    {
        if (SysTimeHasPast(g_conflict.holdTime, 1500)) // 保持时间
        {
            fixChannel(toChannel(g_conflict.channel));
        }
        else
        {
            if (g_conflict.count >= 2)
            {
                if (g_conflict.channel == 0xff)
                {
                    g_conflict.channel = 0;
                }
                else
                {
                    g_conflict.channel++;
                }

                if (g_conflict.channel >= WIRELESS_CHANNEL_COUNT)
                {
                    fixChannel(toChannel(WIRELESS_CHANNEL_COUNT / 2));
                }
                else
                {
                    swtichChannel(toChannel(g_conflict.channel));
                }
            }
        }
    }
}

void WirelessPoll(void)
{
    wirelessDataHandle();
    wirelessConflictDetect();
}
